/*
 * Tencent is pleased to support the open source community by making Mars available.
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the MIT License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://opensource.org/licenses/MIT
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.xinxin.socket.server;

import com.xinxin.socket.handler.ChannelHandlerHolder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
public class ProxyServer {

    private final ChannelHandlerHolder channelHandlerHolder;
    private final int port;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ChannelFuture serverChannelFuture;

    public void shutdown() throws InterruptedException {
        if (serverChannelFuture == null) {
            log.warn("server not start");
            return;
        }

        Channel channel = serverChannelFuture.channel();
        channel.closeFuture().addListener(future -> log.info("server closed, port: {}", port));
        channel.close().sync();

        bossGroup.shutdownGracefully().sync();
        workerGroup.shutdownGracefully().sync();
        log.info("work group closed");
    }

    public ChannelFuture start() {
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializerImpl<SocketChannel>());

            serverChannelFuture = serverBootstrap.bind(port).sync();
            return serverChannelFuture;
        } catch (Exception e) {
            log.error("server bootstrap error", e);
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }

        return null;
    }

    private class ChannelInitializerImpl<C extends Channel> extends ChannelInitializer<C> {

        @Override
        protected void initChannel(C channel) {
            /*
             * 每个ChannelHandler通过add方法加入到ChannelPipeline中去的时候
             * 会创建一个对应的ChannelHandlerContext，并且绑定
             * ChannelPipeline实际维护的是ChannelHandlerContext 的关系
             *
             * 链接：https://www.jianshu.com/p/96a50869b527
             */
            channel.pipeline().addLast(channelHandlerHolder.handlers());
        }
    }
}
